package com.threefish.common;

import com.google.common.base.Stopwatch;
import com.threefish.common.annotation.ErrorMsg;
import com.threefish.common.config.properties.ConfigProperties;
import com.threefish.common.vo.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.nutz.el.El;
import org.nutz.lang.Lang;
import org.nutz.lang.segment.CharSegment;
import org.nutz.lang.util.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author 黄川 huchuc@vip.qq.com
 * @date: 2018/9/30
 **/
@Component
@Aspect
@Slf4j
class ExceptionAspect {

    @Autowired
    ConfigProperties cfg;

    @Pointcut("execution(public * com.threefish.modules..*.*(..))")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object handleControllerMethod(ProceedingJoinPoint pjp) {
        Stopwatch stopwatch = Stopwatch.createStarted();
        Object returnVal;
        try {
            returnVal = pjp.proceed(pjp.getArgs());
        } catch (Throwable throwable) {
            returnVal = handlerException(pjp, throwable);

        } finally {
            log.info("耗时：" + stopwatch.stop().elapsed(TimeUnit.MILLISECONDS) + "(毫秒).");
        }
        return returnVal;
    }

    private ResultVO handlerException(ProceedingJoinPoint pjp, Throwable e) {
        String message = e.getMessage();
        MethodSignature signature = ((MethodSignature) pjp.getSignature());
        Method method = signature.getMethod();
        ErrorMsg annotation = method.getAnnotation(ErrorMsg.class);
        ResultVO vo = ResultVO.error(message);
        if (annotation != null) {
            Context ctx = getContext(signature.getParameterNames(), pjp.getArgs());
            String msg = getElMessage(annotation.value(), ctx);
            if (cfg.getDev()) {
                message = msg + " ！详细错误：" + message;
            } else {
                message = msg;
            }
        }
        log.error("异常{ 方法：" + pjp.getSignature() + "， 参数：" + pjp.getArgs() + ",异常：" + message + "}", e);
        return vo;
    }

    private Context getContext(String[] paramNames, Object[] paranValues) {
        Context ctx = Lang.context();
        for (int i = 0; i < paramNames.length; i++) {
            ctx.set(paramNames[i], paranValues[i]);
        }
        return ctx;
    }

    private String getElMessage(String msg, Context ctx) {
        CharSegment segment = new CharSegment(msg);
        Map<String, El> els = new HashMap<>(0);
        if (segment.hasKey()) {
            els = new HashMap(0);
            for (String key : segment.keys()) {
                els.put(key, new El(key));
            }
        }
        for (String key : segment.keys()) {
            ctx.set(key, els.get(key).eval(ctx));
        }
        return segment.render(ctx).toString();
    }
}

